#include "cline2d.h"

/**
 * CLine2D - creates line identical with x-axis
 */
template <typename T>
CLine2D<T>::CLine2D() {
  CVector2D<T> tmpNormal(0, 1);
  set(tmpNormal, 0);
}
//---------------------------------------------------------------------------

/**
 * CLine2D - copy constructor (deep copy)
 * @param l
 */
template <typename T>
CLine2D<T>::CLine2D(const CLine2D<T> &l) {
  this->copyFrom(l);
}
//---------------------------------------------------------------------------

/**
 * CLine2D - creates line from normal vector and distance from origin
 * @param normal normal vector
 * @param originDist distance from origin
 */
template <typename T>
CLine2D<T>::CLine2D(const CVector2D<T> &normal, T originDist) {
  set(normal, originDist);
}
//---------------------------------------------------------------------------

/**
 * CLine2D - creates line from coordinates of normal and dist. from origin
 * @param normalX
 * @param normalY
 * @param originDist distance from origin
 */
template <typename T>
CLine2D<T>::CLine2D(T normalX, T normalY, T originDist) {
  CVector2D<T> tmpNormal(normalX, normalY);
  set(tmpNormal, originDist);
}
//---------------------------------------------------------------------------

/**
 * CLine2D - creates line from two points
 * @param p1
 * @param p2
 */
template <typename T>
CLine2D<T>::CLine2D(const CVector2D<T> &p1, const CVector2D<T> &p2) {

  // Find normal vector and distance from origin
  CVector2D<T> tmpNormal(p2.getY() - p1.getY(), -(p2.getX() - p1.getX()));
  set(tmpNormal, -(CVector2D<T>::dot(p1, tmpNormal)));
}
//---------------------------------------------------------------------------

/**
 * operator== - compares two lines
 * @param l
 * @return true if lines are identical
 */
template <typename T>
bool CLine2D<T>::operator == (const CLine2D<T> &l) const {

  // Coordinates of normal vectors and distances from origin must be in
  // same ratio
  CVector2D<T> n1(this->normal), n2(l.getNormal());
  T t(originDist / l.getOriginDist());
  return CMath<T>::equals(n1.get(0) / n2.get(0), t) &&
         CMath<T>::equals(n1.get(1) / n2.get(1), t);
}
//---------------------------------------------------------------------------

/**
 * operator!= - compares two lines
 * @param l
 * @return true if lines are not identical
 */
template <typename T>
bool CLine2D<T>::operator != (const CLine2D<T> &l) const {
  return !(*this == l);
}
//---------------------------------------------------------------------------

/**
 * set - sets normal vector and distance from origin
 * @param normal
 * @param originDist
 */
template <typename T>
void CLine2D<T>::set(const CVector2D<T> &normal, T originDist) {

  // Save date, normalize normal vector
  this->normal = normal;
  this->originDist = originDist;
  this->normal.normalize();
}
//---------------------------------------------------------------------------

/**
 * getNormal - returns normal vector
 * @return normal vector
 */
template <typename T>
CVector2D<T> CLine2D<T>::getNormal() const {
  return normal;
}
//---------------------------------------------------------------------------

/**
 * getOriginDist - returns distance from origin
 * @return distance from origin
 */
template <typename T>
T CLine2D<T>::getOriginDist() const {
  return originDist;
}
//---------------------------------------------------------------------------

/**
 * pointDist - calculates distance from given point
 * @param p point
 * @return distance between point and line
 */
template <typename T>
T CLine2D<T>::pointDist(const CVector2D<T> &p) {
  return (CVector2D<T>::dot(p, normal) + originDist) / normal.getNorm();
}
//---------------------------------------------------------------------------

/**
 * lineDist - calculates distance from given line
 * @param l line
 * @return distance between two lines
 */
template <typename T>
T CLine2D<T>::lineDist(const CLine2D<T> &l) {

  // If lines are intersecting, distance is zero
  if (isIntersecting(l)) return 0;

  // Calculate difference from distances from origin
  return fabs(l.getOriginDist() / l.getNormal().getNorm() -
              originDist / normal.getNorm());
}
//---------------------------------------------------------------------------

/**
 * isPointOnLine - checks whether given point lies on line
 * @param p point
 * @return true if point lies on line
 */
template <typename T>
bool CLine2D<T>::isPointOnLine(const CVector2D<T> &p) {
  return CMath<T>::equals(CVector2D<T>::dot(p, normal) + originDist, 0) ?
         true : false;
}
//---------------------------------------------------------------------------

/**
 * calculateIntersectDenominator - calculates intersection denominator
 * @param n1
 * @param n2
 * @return intersection denominator
 */
template <typename T>
T CLine2D<T>::calculateIntersectDenominator(const CVector2D<T> &n1,
    const CVector2D<T> &n2) {

  // If two lines in plane are represented by implicit equations then in
  // coordinates of intersection of these two line stands in both coordinates
  // same denominator
  // If this denominator is zero => both coordinates does not exist =>
  // intersection does not exist
  return n2.getY() * n1.getX() - n2.getX() * n1.getY();
}
//---------------------------------------------------------------------------

/**
 * isIntersecting - test whether this line is intersecting with given line
 * @param l
 * @return true if lines are intersecting
 */
template <typename T>
bool CLine2D<T>::isIntersecting(const CLine2D<T> &l) {
  return CMath<T>::equals(calculateIntersectDenominator(normal,
                          l.getNormal()), 0) ? false : true;
}
//---------------------------------------------------------------------------

/**
 * isIntersecting - test whether this line is intersecting with given segment
 * @param p1 first edge of line segment
 * @param p2 second edge of line segment
 * @return true if line is intersecting with line segment
 */
template <typename T>
bool CLine2D<T>::isIntersecting(const CVector2D<T> &p1,
                                const CVector2D<T> &p2) {

  // Distance of segment's edges from line
  T dist1(CVector2D<T>::dot(normal, p1) + originDist);
  T dist2(CVector2D<T>::dot(normal, p2) + originDist);

  // If distances has same sign => both edges lie in same half-plane =>
  // line segment does not intersect with line
  if (dist1 * dist2 >= 0) return false;
  return true;
}
//---------------------------------------------------------------------------

/**
 * isParalell - checks whether this line is paralell with given line
 * @param l
 * @return true if lines are parallel
 */
template <typename T>
bool CLine2D<T>::isParalell(const CLine2D<T> &l) {

  // If two lines in plane are not intersecting => they are paralell
  return CMath<T>::equals(calculateIntersectDenominator(normal,
                          l.getNormal()), 0) ? true : false;
}
//---------------------------------------------------------------------------

/**
 * findIntersection - finds intersection of two lines
 * @param l
 * @return line intersection
 */
template <typename T>
CVector2D<T> CLine2D<T>::findIntersection(const CLine2D<T> &l) {

  // Temporary variables
  CVector2D<T> result;
  CVector2D<T> n1(this->normal), n2(l.getNormal());
  T d1(this->originDist), d2(l.getOriginDist());

  // Calculate denominator for coordinates of intersection
  T tmp(calculateIntersectDenominator(n1, n2));

  // If denominator is zero, intersection does not exist, exception
  if (CMath<T>::equals(tmp, 0)) {
    throw CIntersectNotExistException(getClassName(), "findIntersection");
  }

  // Calculate intersection coordinates
  result.setX((d2 * n1.get(1) - d1 * n2.get(1)) / tmp);
  result.setY((d1 * n2.get(0) - d2 * n1.get(0)) / tmp);
  return result;
}
//---------------------------------------------------------------------------

/**
 * copyFrom - copies line from another
 * @param l
 */
template <typename T>
void CLine2D<T>::copyFrom(const CLine2D<T> &l) {
  this->originDist = l.getOriginDist();
  this->normal = l.getNormal();
}
//---------------------------------------------------------------------------

template class CVector2D<char>;
template class CVector2D<short>;
template class CVector2D<int>;
template class CVector2D<float>;
template class CVector2D<double>;
